import { Injectable } from "@angular/core";

import { MockParas, Where, WhereEnum } from "../model/parameters";
import { IProvider } from "../provider.interface";
import { LoggerService, LoggerInfo } from '../../logger/logger.service';
import { Utility } from '../../core/utility';
import { Http } from '@angular/http';

@Injectable()
export class MockProvider implements IProvider {
    data: any[] = [];
    servicePara: any;
    logger:LoggerService;
    constructor(serviceParas: any, data: any) {
        this.logger = new LoggerService();
        this.data = data.data;
        this.servicePara = serviceParas;
    }

    set(http: Http): void {

    }

    // start =====  general functions of RESTFul & Websocket & Mock =====
    query(paras: MockParas, sucCallback: Function, failCallback: Function): void {
        try {
            this._get(paras, sucCallback, failCallback);
        } catch (error) {
            this.logger.error(new LoggerInfo("framework/provider/MockProvider.query", "this.get failed", error));
            if (failCallback) {
                failCallback(error);
            }
        }
    }

    get(paras: MockParas, sucCallback: Function, failCallback: Function): void {
        try {
            this._get(paras, sucCallback, failCallback);
        } catch (error) {
            this.logger.error(new LoggerInfo("framework/provider/MockProvider.get", "this.get failed", error));
            if (failCallback) {
                failCallback(error);
            }
        }
    }

    add(paras: MockParas, sucCallback: Function, failCallback: Function): void {
        try {
            this._add(paras, sucCallback, failCallback);
        } catch (error) {
            this.logger.error(new LoggerInfo("framework/provider/MockProvider.add", "this.add failed", error));
            if (failCallback) {
                failCallback(error);
            }
        }
    }

    update(paras: MockParas, sucCallback: Function, failCallback: Function): void {
        try {
            this._update(paras, sucCallback, failCallback);
        } catch (error) {
            this.logger.error(new LoggerInfo("framework/provider/MockProvider.update", "this.update failed", error));
            if (failCallback) {
                failCallback(error);
            }
        }
    }

    delete(paras: MockParas, sucCallback: Function, failCallback: Function): void {
        try {
            this._delete(paras, sucCallback, failCallback);
        } catch (error) {
            this.logger.error(new LoggerInfo("framework/provider/MockProvider.delete", "this.delete failed", error));
            if (failCallback) {
                failCallback(error);
            }
        }
    }
    // end ===== general functions of RESTFul & Websocket =====  

    // start ===== special functions of Websocket =====
    invokeProxy(paras: any, sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/MockProvider.invokeProxy", "Mock provider no need to implement this function.", null));
        failCallback("Mock provider no need to implement this function.");
    }

    stopRequest(requestId: number, sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/MockProvider.stopRequest", "Mock provider no need to implement this function.", null));
        failCallback("Mock provider no need to implement this function.");
    }

    stopAllRequests(sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/MockProvider.stopAllRequests", "Mock provider no need to implement this function.", null));
        failCallback("Mock provider no need to implement this function.");
    }

    closeConnection(sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/MockProvider.closeConnection", "Mock provider no need to implement this function.", null));
        failCallback("Mock provider no need to implement this function.");
    }

    openConnection(token: string, sucCallback: Function, failCallback: Function): void {
        this.logger.error(new LoggerInfo("framework/provider/MockProvider.openConnection", "Mock provider no need to implement this function.", null));
        failCallback("Mock provider no need to implement this function.");
    }
    // end ===== special functions of Websocket =====

    private _get(paras: MockParas, sucCallback: Function, failedCallback: Function): void {
        if (paras != undefined) {
            let where: Where[] = this._parasConvertor(paras.where);
            let result: any[] = this._searchItems(where);
            sucCallback(result);
        } else {
            failedCallback(paras);
        }
    }

    private _add(paras: MockParas, sucCallback: Function, failedCallback: Function): void {
        let isExist: boolean = this._checkIfExist(this._parasConvertor(paras.where));
        if (isExist && paras.where !== undefined && paras.where !== "") {
            failedCallback('Exists');
        } else {
            this.data.push(paras.items);
            sucCallback(this.data);
        }
    }

    private _delete(paras: MockParas, sucCallback: Function, failedCallback: Function): void {
        let result: any[] = this._searchItems(this._parasConvertor(paras.where));
        if (result.length <= 0) {
            failedCallback('Not Exists');
        } else {
            // findIndex is callback Fn.
            let privateData:any[] = Utility.deepCloneObj(this.data, true);
            for (let item of result) {
                let index: number = privateData.findIndex(x => x.mockid === item.mockid);
                if (index > -1){
                    privateData.splice(index, 1);
                }
            }
            sucCallback(privateData);
        }
    }

    private _update(paras: MockParas, sucCallback: Function, failedCallback: Function): void {
        let result: any[] = this._searchItems(this._parasConvertor(paras.where));
        if (result.length <= 0 || paras.items === undefined || paras.items.length <= 0) {
            failedCallback('Not Exists or nothing to update');
        } else {
            // findIndex is callback Fn.
            let privateData:any[] = Utility.deepCloneObj(this.data, true);
            for (let item of result) {
                let index: number = privateData.findIndex(x => x.mockid === item.mockid);
                let mockid: number = item.mockid;
                if (index > -1){
                    privateData[index] = paras.items[0];
                    //restore mock_id and promise it's will not be override.
                    privateData[index].mockid = mockid;
                }
            }
            sucCallback(privateData);
        }
    }

    private _parasConvertor(whereString: string): Where[] {
        if (whereString == undefined) return [];
        let result: Where[] = [];
        let whereArray: string[] = whereString.split('and');
        for (let item of whereArray) {
            let resultItem: Where = new Where();
            if (item.indexOf('=') > -1) {
                resultItem.whereOperator = WhereEnum.equal;
                resultItem.whereName = item.split('=')[0].trim();
                resultItem.whereValue = item.split('=')[1].trim().replace(/\'/g, '').replace(/\"/g, '');
            }
            else if (item.indexOf('in') > -1) {
                resultItem.whereOperator = WhereEnum.in;
                resultItem.whereName = item.split('in')[0].trim();
                resultItem.whereValue = item.split('in')[1].trim().replace(/\'/g, '').replace(/\"/g, '');
            }
            //Only support '=', 'IN' operator currently.
            //TODO: will suport '>','<','!='  in a short future.
            else {

            }
            result.push(resultItem);
        }
        return result;
    }

    private _checkIfExist(whereArray: Where[]): boolean {
        let result: boolean = true;
        let media: any;
        // filter is callback Fn.
        let privateData = Utility.deepCloneObj(this.data, true);
        for (let where of whereArray) {
            switch (where.whereOperator) {
                case WhereEnum.equal:
                    if (media == undefined) {
                        let resultE = privateData.filter(x => x[where.whereName] === where.whereValue);
                        if (resultE != undefined && resultE.length > 0) {
                            media = resultE;
                        } else {
                            result = false;
                        }
                        break;
                    }
                    else {
                        let resultE = media.filter(x => x[where.whereName] === where.whereValue);
                        if (resultE != undefined && resultE.length > 0) {
                            media = resultE;
                        } else {
                            result = false;
                        }
                        break;
                    }
                case WhereEnum.in:
                    let paraArray = where.whereValue.split(',');
                    if (media == undefined) {
                        let resultI = privateData.filter(x => paraArray.includes(x[where.whereName]));
                        if (resultI != undefined && resultI.length > 0) {
                            media = resultI;
                        }
                        else {
                            result = false;
                        }
                        break;
                    }
                    else {
                        let resultI = media.filter(x => paraArray.includes(x[where.whereName]));
                        if (resultI != undefined && resultI.length > 0) {
                            media = resultI;
                        }
                        else {
                            result = false;
                        }
                        break;
                    }
                //Only support '=', 'IN' operator currently.
                //TODO: will suport '>','<','!='  in a short future.
                default:
                    result = false;
                    break;
            }
            //Only support 'AND' operator currently.
            //TODO: will suport 'OR' in a short future.
            if (!result)
                break;
        }
        return result;
    }

    private _searchItems(whereArray: Where[]): any[] {
        let result: any[];
        let failFlag = true;
        // filter is callback Fn.
        let privateData = Utility.deepCloneObj(this.data, true);
        for (let where of whereArray) {
            switch (where.whereOperator) {
                case WhereEnum.equal:
                    if (result == undefined) {
                        let resultE = privateData.filter(x => x[where.whereName] === where.whereValue);
                        if (resultE != undefined && resultE.length > 0) {
                            result = resultE;
                        } else {
                            failFlag = false;
                        }
                    }
                    else {
                        let resultE = result.filter(x => x[where.whereName] === where.whereValue);
                        if (resultE != undefined && resultE.length > 0) {
                            result = resultE;
                        } else {
                            failFlag = false;
                        }
                    }
                    break;
                case WhereEnum.in:
                    let paraArray = where.whereValue.split(',');
                    if (result == undefined) {
                        let resultI = privateData.filter(x => paraArray.includes(x[where.whereName]));
                        if (resultI != undefined && resultI.length > 0) {
                            result = resultI;
                        }
                        else {
                            failFlag = false;
                        }
                    }
                    else {
                        let resultI = result.filter(x => paraArray.includes(x[where.whereName]));
                        if (resultI != undefined && resultI.length > 0) {
                            result = resultI;
                        }
                        else {
                            failFlag = false;
                        }
                    }
                    break;
                //Only support '=', 'IN' operator currently.
                //TODO: will suport '>','<','!='  in a short future.
                default:
                    failFlag = false;
                    break;
            }

            //Only support 'AND' operator currently.
            //TODO: will suport 'OR' in a short future.
            if (!result)
            {
                result = [];
                break;
            }
        }
        return result;
    }

    private _ANDCALCULATE(...condition) {
        return (m, n) => this.data.findIndex(x => x[m.WhereName] === m.WhereValue && x[n.WhereName] === n.WhereValue);
    }

    private _ORCALCULATE(...condition) {
        return (m, n) => this.data.findIndex(x => x[m.WhereName] === m.WhereValue || x[n.WhereName] === n.WhereValue);
    }

}